package com.umeox.sdk_ring

import android.bluetooth.BluetoothGatt
import android.os.Handler
import android.os.HandlerThread
import com.goodix.ble.gr.lib.com.ILogger
import com.goodix.ble.gr.lib.dfu.v2.DfuProgressListener
import com.goodix.ble.gr.lib.dfu.v2.EasyDfu2
import com.umeox.sdk_ring.core.*
import com.umeox.sdk_ring.protocol.IAppProtocol
import com.umeox.sdk_ring.scan.RingScanner
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.lang.Error

class RingDeviceInteraction(
    private val macAddress: String,
    private val appProtocolImpl: IAppProtocol
) : RingScanCallback {

    companion object {
        private const val TAG = "RingDeviceInteraction"
        private const val TIME_OUT_DURATION = 30 * 1000L
    }

    //交互子线程
    private val interactionThread = HandlerThread(macAddress).apply {
        start()
    }

    ///当前线程的Handler
    private var handler: Handler? = null

    //设置连接器
    private val bleConnector = RingBLEConnector(this)

    //指令解析器
    private val ringCommandParser = RingCommandParser(appProtocolImpl)

    //OTA升级的工具类
    private val ringOTAUpgradeTool = RingOTAUpgradeTool3(this)

    //戒指接口实现者
    private val ringProtocolImpl = DefaultRingProtocolImpl(this)

    //扫描器
    private val scanner = RingScanner(this, this, Handler(interactionThread.looper))

    //是否处于连接状态中 防止多次回调连接
    private var connecting = false

    //已被扫描到  开始处理了
    private var scanned = false

    //判断是否需要自动重连
    private var isNeedAutoReConnect = false

    //当前连接状态
    private var connectedState = false

    //是否在断开连接后关闭线程
    private var quit = false

    //连接超时处理
    private var connectTimeoutRunnable = Runnable {
        synchronized(connecting) {
            connecting = false
            scanner.stopScan()
            appProtocolImpl.onConnectTimeout()
        }
    }

    //连接
    fun connectDevice() {
        UMRingSdk.log(TAG, "连接到设备 $macAddress  connecting = $connecting")
        synchronized(connecting) {
            if (connecting) return
            connecting = true
            appProtocolImpl.onConnecting()
            isNeedAutoReConnect = true
            scanned = false
            scanner.startScan()
            sendMessageDelayed(connectTimeoutRunnable, TIME_OUT_DURATION)
        }
    }

    fun directConnectDevice() {
        UMRingSdk.log(TAG, "直接连接到设备 $macAddress  connecting = $connecting")
        synchronized(connecting) {
            if (connecting) return
            connecting = true
            appProtocolImpl.onConnecting()
            isNeedAutoReConnect = false
            bleConnector.connect(macAddress)
            sendMessageDelayed(connectTimeoutRunnable, TIME_OUT_DURATION)
        }
    }

    //断开连接
    fun disConnectDevice() {
        UMRingSdk.log(TAG, "断开设备 $macAddress 连接")
        synchronized(connecting) {
            isNeedAutoReConnect = false
            connecting = false
            scanner.stopScan()
            bleConnector.disConnect()
        }
    }

    //销毁当前设备
    fun destroy() {
        sendMessage {
            UMRingSdk.log(TAG, "断开连接，并关闭线程 当前连接状态：$connectedState")
            if (connectedState) {
                quit = true
                bleConnector.disConnect()
            } else {
                interactionThread.quitSafely()
            }
        }
    }

    //获取戒指协议实现者
    fun getDefaultRingProtocolImpl() = ringProtocolImpl

    //写入指令
    fun writeCommand(cmd: String, commandDesc: String) {
        sendMessage {
            val result = bleConnector.writeCommand(cmd)
            UMRingSdk.log(TAG, "$commandDesc -> 写入指令：$cmd     写入结果：$result")
        }
    }

    /**
     * 发送消息
     */
    private fun sendMessage(runnable: Runnable) {
        if (handler == null) {
            handler = Handler(interactionThread.looper)
        }
        handler?.post(runnable)
    }

    /**
     * 发送延时消息
     */
    private fun sendMessageDelayed(runnable: Runnable, delayMillis: Long) {
        if (handler == null) {
            handler = Handler(interactionThread.looper)
        }
        handler?.postDelayed(runnable, delayMillis)
    }

    /**
     * 移除任务
     */
    private fun removeMessage(runnable: Runnable) {
        if (handler == null) {
            handler = Handler(interactionThread.looper)
        }
        handler?.removeCallbacks(runnable)
    }

    fun onDisconnecting() {
        synchronized(connecting) {
            connecting = false
            scanner.stopScan()
            appProtocolImpl.onDisconnecting()
        }
    }

    fun onDisconnected(bleDisConnectType: BleDisConnectType) {
        synchronized(connecting) {
            connecting = false
            connectedState = false
            removeMessage(connectTimeoutRunnable)
            scanner.stopScan()
            appProtocolImpl.onDisconnected()
            ringOTAUpgradeTool.onBleDisConnect()

            if (quit) {
                destroy()
            } else {
                //如果是非主动断开 并且 非蓝牙关闭断开的情况下  需要自动重连
                if (isNeedAutoReConnect && bleDisConnectType == BleDisConnectType.GATT_DISCONNECT) {
                    UMRingSdk.log(TAG, "开始自动重连")
                    connecting = true
                    scanned = false
                    scanner.startScan()
                }
            }
        }
    }

    fun onConnected() {
        synchronized(connecting) {
            connecting = false
            connectedState = true
            scanner.stopScan()
            removeMessage(connectTimeoutRunnable)
            appProtocolImpl.onConnected()
        }
    }

    fun onCharacteristicChanged(value: ByteArray?) {
        value?.let {
            ringOTAUpgradeTool.setReceiveValue(value)
            ringCommandParser.handlerReceiveResult(String(it))
        }
    }

    fun onMtuChanged(status: Int, mtu: Int) {
        UMRingSdk.log(TAG, "收到mtu信息回调 status：$status  mtu：$mtu")
        if (BluetoothGatt.GATT_SUCCESS == status) {
            ringOTAUpgradeTool.setMtuChangeSateAndValue(true, mtu)
        } else {
            ringOTAUpgradeTool.setMtuChangeSateAndValue(true, 235)
        }
    }

    fun onCharacteristicWrite(status: Int) {
        ringOTAUpgradeTool.setWriteStatus(status)
    }

    fun setBleOTANotify() {
        bleConnector.setBleOTANotify()
    }

    fun writeLongCommand(
        type: Int,
        address: Int,
        buffer: ByteArray?,
        length: Long
    ): Boolean {
        return bleConnector.writeLongCommand(type, address, buffer, length)
    }

    fun writeIntCommand(
        type: Int,
        address: Int,
        buffer: ByteArray?,
        length: Int
    ): Boolean {
        return bleConnector.writeIntCommand(type, address, buffer, length)
    }

    fun requestMtu(mtu: Int) {
        bleConnector.requestMtu(mtu)
    }

    fun startOTA(
        file: File,
        isSafeOTA: Boolean,
        listener: RingOTAUpgradeListener
    ) {
        sendMessage {
            runBlocking {
                ringOTAUpgradeTool.init(file, listener)
                delay(2000)
                bleConnector.requestConnectionPriority()
                if (isSafeOTA) {
                    delay(3000)
                } else {
                    delay(1000)
                }
                ringOTAUpgradeTool.startUpgrade(isSafeOTA)
            }
        }
    }

    fun startHDOTA(file: File, listener: RingOTAUpgradeListener) {
        val device = bleConnector.getTargetDevice()
        if (device != null) {
            val stream: InputStream = FileInputStream(file)
            val dfu2 = EasyDfu2()

            val copyAddress: Int = "1080000".toInt(16)
            dfu2.setLogger(object : ILogger {
                override fun v(tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "v - $tag - $msg"
                    )
                }

                override fun d(tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "d - $tag - $msg"
                    )
                }

                override fun i(tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "i - $tag - $msg"
                    )
                }

                override fun w(tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "w - $tag - $msg"
                    )
                }

                override fun e(tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "e - $tag - $msg"
                    )
                }

                override fun logRaw(timestamp: Long, level: Int, tag: String?, msg: String?) {
                    UMRingSdk.log(
                        "HD_OTA_LOG",
                        "logRaw - $tag - $msg"
                    )
                }

            })
            dfu2.setListener(object : DfuProgressListener {
                override fun onDfuStart() {
                    listener.upgradeStart()
                }

                override fun onDfuProgress(percent: Int, speed: Int, message: String?) {
                    listener.upgradeProgress(percent)
                }

                override fun onDfuComplete() {
                    listener.upgradeSuccessful()
                }

                override fun onDfuError(message: String?, error: Error?) {
                    listener.upgradeFail()
                }

            })
            dfu2.setFastMode(false)
            dfu2.startDfuInCopyMode(UMRingSdk.getApp(), device, stream, copyAddress) // use different mode

        } else {
            listener.upgradeFail()
        }
    }

    override fun onScanResult(result: UMScanResult) {
        if (result.device.address.equals(macAddress)) {
            synchronized(connecting) {
                synchronized(scanned) {
                    UMRingSdk.log(
                        TAG,
                        "自动重连扫描到设备 当前连接状态：connecting = $connecting   macAddress = $macAddress scanned = $scanned"
                    )
                    if (!scanned && connecting) {
                        scanned = true
                        bleConnector.connect(macAddress)
                    }
                }
            }
        }
    }
}